#region License
/* **********************************************************************************
 * Copyright (c) Roman Ivantsov
 * This source code is subject to terms and conditions of the MIT License
 * for Irony. A copy of the license can be found in the License.txt file
 * at the root of this distribution. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * MIT License.
 * You must not remove this notice from this software.
 * **********************************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Irony.Parsing
{

    internal class IntList : List<int> { }

    public class NonTerminal : BnfTerm
    {

        #region Constructors

        public NonTerminal(string name) : base(name, name) { }

        public NonTerminal(string name, string errorAlias) : base(name, errorAlias) { }

        public NonTerminal(string name, BnfExpression expression)
            : this(name)
        {
            Rule = expression;
        }

        #endregion

        #region properties/fields: Rule, ErrorRule

        public BnfExpression Rule;
        // Separate property for specifying error expressions. This allows putting all such expressions in a separate section
        // in grammar for all non-terminals. However you can still put error expressions in the main Rule property, just like
        // in YACC
        public BnfExpression ErrorRule;

        // A template for representing ParseTreeNode in the parse tree. Can contain '#{i}' fragments referencing 
        // child nodes by index
        public string NodeCaptionTemplate;

        //Converted template with index list
        private string _convertedTemplate;
        private IntList _captionParameters;

        // Productions are used internally by Parser builder
        internal ProductionList Productions = new ProductionList();
        #endregion

        #region Events: Reduced
        //Note that Reduced event may be called more than once for a List node 
        public event EventHandler<ReducedEventArgs> Reduced;
        internal void OnReduced(ParsingContext context, Production reducedProduction, ParseTreeNode resultNode)
        {
            if (Reduced != null)
                Reduced(this, new ReducedEventArgs(context, reducedProduction, resultNode));
        }
        #endregion

        #region overrides: ToString, Init
        public override string ToString()
        {
            return Name;
        }
        public override void Init(GrammarData grammarData)
        {
            if (Rule != null)
                Rule.Normalize();

            base.Init(grammarData);
            if (!string.IsNullOrEmpty(NodeCaptionTemplate))
                ConvertNodeCaptionTemplate();
        }
        #endregion

        // Contributed by Alexey Yakovlev (yallie)
        #region Grammar hints
        // Adds a hint at the end of all productions
        public void AddHintToAll(GrammarHint hint)
        {
            if (this.Rule == null)
                throw new Exception("Rule property must be set on non-terminal before calling AddHintToAll.");
            foreach (var plusList in this.Rule.Data)
                plusList.Add(hint);
        }

        #endregion

        #region NodeCaptionTemplate utilities
        //We replace original tag '#{i}'  (where i is the index of the child node to put here)
        // with the tag '{k}', where k is the number of the parameter. So after conversion the template can 
        // be used in string.Format() call, with parameters set to child nodes captions
        private void ConvertNodeCaptionTemplate()
        {
            _captionParameters = new IntList();
            _convertedTemplate = NodeCaptionTemplate;
            var index = 0;
            while (index < 100)
            {
                var strParam = "#{" + index + "}";
                if (_convertedTemplate.Contains(strParam))
                {
                    _convertedTemplate = _convertedTemplate.Replace(strParam, "{" + _captionParameters.Count + "}");
                    _captionParameters.Add(index);
                }
                if (!_convertedTemplate.Contains("#{")) return;
                index++;
            }//while
        }//method

        public string GetNodeCaption(ParseTreeNode node)
        {
            var paramValues = new string[_captionParameters.Count];
            for (int i = 0; i < _captionParameters.Count; i++)
            {
                var childIndex = _captionParameters[i];
                if (childIndex < node.ChildNodes.Count)
                {
                    var child = node.ChildNodes[childIndex];
                    //if child is a token, then child.ToString returns token.ToString which contains Value + Term; 
                    // in this case we prefer to have Value only
                    paramValues[i] = (child.Token != null ? child.Token.ValueString : child.ToString());
                }
            }
            var result = string.Format(_convertedTemplate, paramValues);
            return result;
        }
        #endregion

    }//class

    public class NonTerminalList : List<NonTerminal>
    {
        public override string ToString()
        {
            return string.Join(" ", this);
        }
    }

    public class NonTerminalSet : HashSet<NonTerminal>
    {
        public override string ToString()
        {
            return string.Join(" ", this);
        }
    }


}//namespace
